﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;
using System.Diagnostics;
using System.IO.Ports;
using System.Management;
using System.Windows.Forms;
using System.Threading;

namespace LevSTProgramTransform.Driver
{
    public sealed class USBDetect : IDisposable
    {
        /// <summary>
        /// USB插入事件監視
        /// </summary>
        private ManagementEventWatcher insertWatcher = null;
        /// <summary>
        /// USB拔出事件監視
        /// </summary>
        private ManagementEventWatcher removeWatcher = null;
        static ManagementObjectSearcher comPortSearcher;
        /// <summary>
        /// 添加USB事件監視器
        /// </summary>
        /// <param name="usbInsertHandler">USB插入事件處理器</param>
        /// <param name="usbRemoveHandler">USB拔出事件處理器</param>
        /// <param name="withinInterval">發送通知允許的滯後時間</param>
        public Boolean AddUSBEventWatcher(EventArrivedEventHandler usbInsertHandler, EventArrivedEventHandler usbRemoveHandler, TimeSpan withinInterval)
        {
            try
            {
                ManagementScope Scope = new ManagementScope("root\\CIMV2");
                Scope.Options.EnablePrivileges = true;
                // USB插入監視
                if (usbInsertHandler != null)
                {
                    WqlEventQuery InsertQuery = new WqlEventQuery("__InstanceCreationEvent",
                        withinInterval,
                        "TargetInstance isa 'Win32_USBControllerDevice'");
                    insertWatcher = new ManagementEventWatcher(Scope, InsertQuery);
                    insertWatcher.EventArrived += usbInsertHandler;
                    insertWatcher.Start();
                }
                // USB拔出監視
                if (usbRemoveHandler != null)
                {
                    WqlEventQuery RemoveQuery = new WqlEventQuery("__InstanceDeletionEvent",
                        withinInterval,
                        "TargetInstance isa 'Win32_USBControllerDevice'");
                    removeWatcher = new ManagementEventWatcher(Scope, RemoveQuery);
                    removeWatcher.EventArrived += usbRemoveHandler;
                    removeWatcher.Start();
                }
                return true;
            }
            catch (Exception)
            {
                RemoveUSBEventWatcher();
                return false;
            }
        }
        /// <summary>
        /// 移去USB事件監視器
        /// </summary>
        public void RemoveUSBEventWatcher()
        {
            if (insertWatcher != null)
            {
                insertWatcher.Stop();
                insertWatcher = null;
            }
            if (removeWatcher != null)
            {
                removeWatcher.Stop();
                removeWatcher = null;
            }
        }
        /// <summary>
        /// USB控制設備類型
        /// </summary>
        public struct USBControllerDevice
        {
            /// <summary>
            /// USB控制器設備ID
            /// </summary>
            public String Antecedent;
            /// <summary>
            /// USB即插即用設備ID
            /// </summary>
            public String Dependent;
        }
        /// <summary>
        /// 定位發生插拔的USB設備
        /// </summary>
        /// <param name="e">USB插拔事件參數</param>
        /// <returns>發生插拔現象的USB控制設備ID</returns>
        public static List<USBControllerDevice> WhoUSBControllerDevice(EventArrivedEventArgs e)
        {
            //foreach (PropertyData Property in WMIClass.Properties)
            //    ListCollection.Add(new ListViewItem(new string[] { "Property",
            //                   Property.Name,GetPropertyValue(Property) }));

            ManagementBaseObject mbo = e.NewEvent["TargetInstance"] as ManagementBaseObject;
            if (mbo != null && mbo.ClassPath.ClassName == "Win32_USBControllerDevice")
            {
                String Antecedent = (mbo["Antecedent"] as String).Replace("\"", String.Empty).Split(new Char[] { '=' })[1];
                String Dependent = (mbo["Dependent"] as String).Replace("\"", String.Empty).Split(new Char[] { '=' })[1];
                return new List<USBControllerDevice>(1) { new USBControllerDevice { Antecedent = Antecedent, Dependent = Dependent } };
            }
            return null;
        }
        /// <summary>
        ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            //FiresTheMessageEvent("Dispose");
            // Stop the WMI monitors when this instance is disposed.
            RemoveUSBEventWatcher();
        }
        /// <summary>
        ///     Dynamically populates the SerialPorts property with relevant devices discovered from the WMI Win32_SerialPorts class.
        /// </summary>
        public static Dictionary<string, string> DiscoverComPort()
        {
            // Create a temporary dictionary to superimpose onto the SerialPorts property.
            Dictionary<string, string> dict = new Dictionary<string, string>();

            try
            {
                // Scan through each SerialPort registered in the WMI.
                foreach (ManagementObject device in
                    new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_SerialPort").Get())
                {
                    //("device:" + device["PNPDeviceID"].ToString()); //ex: USB\VID_2047&PID_0301\A4F3984627000F00 ==>vid/pid/guid
                    //("device:" + device["Description"].ToString()); //ex: Dynapack Virtual COM Port (CDC)
                    //("device:" + device["DeviceID"].ToString());    //ex: COM48
                    //("device:" + device["Name"].ToString());    //ex: Dynapack Virtual COM Port (CDC) (COM48)
                    //("device:" + device["SystemName"].ToString());  //ex: DP1_HSINMO_LIN ==>owner computer name
                    //("device:" + device["Caption"].ToString()); //ex: Dynapack Virtual COM Port (CDC) (COM48)

                    //// Gather related configuration details for the Arduino Device.
                    //var config = device.GetRelated("Win32_SerialPortConfiguration").Cast<ManagementObject>().ToList().FirstOrDefault();
                    //// Set the SerialPort's BaudRate property. Use the devices maximum BaudRate as a fallback.
                    //port.BaudRate = (config != null)
                    //                    ? int.Parse(config["BaudRate"].ToString())
                    //                    : int.Parse(device["MaxBaudRate"].ToString());

                    try
                    {
                        // Set the SerialPort's PortName property.
                        string portName = device["DeviceID"].ToString();
                        string portDescription = device["Description"].ToString();
                        // Add the SerialPort to the dictionary. Key = Arduino device description.
                        dict.Add(portName, portDescription);
                    }
                    catch (ArgumentException)
                    {
                        Debug.WriteLine("An element with Key = \"txt\" already exists.");
                    }
                }

                // Return the dictionary.
                //SerialPorts = dict;
            }
            catch (ManagementException mex)
            {
                // Send a message to debug.
                Debug.WriteLine(@"An error occurred while querying for WMI data: " + mex.Message);
            }
            return dict;

        }
        public static Dictionary<string, string> DiscoverComPort_2()
        {
            //Thread.Sleep(3000);
            Dictionary<string, string> dict = new Dictionary<string, string>();
            //Below is code pasted from WMICodeCreator
            try
            {
                foreach (ManagementObject queryObj in
                    new ManagementObjectSearcher(@"root\WMI", "SELECT * FROM MSSerial_PortName").Get())
                {
                    //Console.WriteLine("-----------------------------------");
                    //Console.WriteLine("MSSerial_PortName instance"); 
                    //Console.WriteLine("-----------------------------------");
                    //Console.WriteLine("InstanceName: {0}", queryObj["InstanceName"]);

                    //Console.WriteLine("-----------------------------------");
                    //Console.WriteLine("MSSerial_PortName instance");
                    //Console.WriteLine("-----------------------------------");
                    //Console.WriteLine("PortName: {0}", queryObj["PortName"]);


                    //If the serial port's instance name contains USB it must be a USB to serial device
                    //if (queryObj["InstanceName"].ToString().Contains("USB"))
                    //{
                    //    Console.WriteLine(queryObj["PortName"] + " is a USB to SERIAL adapter/converter");
                    //}                   
                    try
                    {
                        // Set the SerialPort's PortName property.
                        string portName = queryObj[@"PortName"].ToString();
                        string portDescription = queryObj[@"InstanceName"].ToString();
                        //if (queryObj[@"InstanceName"].ToString().Contains("USB"))
                        if (queryObj[@"InstanceName"].ToString().Contains(@"VID_067B&PID_2303"))
                        {
                            portDescription =  "Prolific USB-to-Serial Comm Port";
                        }
                        //Add the SerialPort to the dictionary. Key = Arduino device description.
                        dict.Add(portName, portDescription);
                    }
                    catch (ArgumentException)
                    {
                        Debug.WriteLine("An element with Key = \"txt\" already exists.");
                        //return null;
                    }
                }
            }
            catch (ManagementException e)
            {
                MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
                //return null;
            }
            return dict;
        }
        public string Name { get; set; }
        public string Description { get; set; }
        public USBDetect() { }
        public static Dictionary<string, string> DiscoverComPort_3()
        {
            //Thread.Sleep(3000);
            
            Dictionary<string, string> dict = new Dictionary<string, string>();
            string[] comports = SerialPort.GetPortNames();

            //Below is code pasted from WMICodeCreator
            //ConnectionOptions options = ProcessConnection.ProcessConnectionOptions();
            //ManagementScope connectionScope = ProcessConnection.ConnectionScope(Environment.MachineName, options, @"\root\CIMV2");
            //ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");
            //ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(connectionScope, objectQuery);
            comPortSearcher = new ManagementObjectSearcher(@"\root\CIMV2", "SELECT * FROM Win32_PnPEntity WHERE Caption like '%(COM%'");
            using (comPortSearcher)
            {
                try
                {
                    foreach (ManagementObject obj in comPortSearcher.Get())
                    {
                        string objCaption = obj["Caption"].ToString();
                        for (int i = 0; i < comports.GetLength(0); i++)
                        {
                            if (objCaption.Contains(comports[i]))
                            {
                                dict.Add(comports[i], objCaption);
                                break;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    return null;
                }
            }
            return dict;
        }
        //// 在主程式呼時用的event
        //private void USBEventHandler(Object sender, EventArrivedEventArgs e)
        //{
        //    if (e.NewEvent.ClassPath.ClassName == "__InstanceCreationEvent")
        //    {
        //        G_MsgShow.Msg(MsgType.Incoming, "USB插入時間：" + DateTime.Now + Environment.NewLine);
        //    }
        //    else if (e.NewEvent.ClassPath.ClassName == "__InstanceDeletionEvent")
        //    {
        //        G_MsgShow.Msg(MsgType.Incoming, "USB拔出時間：" + DateTime.Now + Environment.NewLine);
        //    }
        //    foreach (USB_DetectingEvent.USBControllerDevice Device in USB_DetectingEvent.WhoUSBControllerDevice(e))
        //    {
        //        G_MsgShow.Msg(MsgType.Incoming, "\tAntecedent：" + Device.Antecedent + Environment.NewLine);
        //        G_MsgShow.Msg(MsgType.Incoming, "\tDependent：" + Device.Dependent + Environment.NewLine);
        //    }
        //}

    }//public sealed class USBDetect : IDisposable
    internal class ProcessConnection
    {
        public static ConnectionOptions ProcessConnectionOptions()
        {
            ConnectionOptions options = new ConnectionOptions();
            options.Impersonation = ImpersonationLevel.Impersonate;
            options.Authentication = AuthenticationLevel.Default;
            options.EnablePrivileges = true;
            return options;
        }
        public static ManagementScope ConnectionScope(string machineName, ConnectionOptions options, string path)
        {
            ManagementScope connectScope = new ManagementScope();
            connectScope.Path = new ManagementPath(@"\\" + machineName + path);
            connectScope.Options = options;
            connectScope.Connect();
            return connectScope;
        }
    }//internal class ProcessConnection
}//namespace LevSTProgramTransform.Driver
